package com.sjtu.vfact.visualization.view;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Paint;
import java.awt.geom.Point2D;
import java.lang.reflect.Constructor;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.collections15.functors.ConstantTransformer;
import org.apache.commons.collections15.functors.MapTransformer;
import org.apache.commons.collections15.map.LazyMap;

import com.sjtu.vfact.textual.model.Cluster;
import com.sjtu.vfact.textual.model.Document;
import com.sjtu.vfact.textual.model.MasterTermset;
import com.sjtu.vfact.textual.model.TermSet;
import com.sjtu.vfact.visualization.base.VfactEdge;
import com.sjtu.vfact.visualization.base.VfactVertex;
import com.sjtu.vfact.visualization.util.VizUtil;

import edu.uci.ics.jung.algorithms.layout.AggregateLayout;
import edu.uci.ics.jung.algorithms.layout.CircleLayout;
import edu.uci.ics.jung.algorithms.layout.FRLayout;
import edu.uci.ics.jung.algorithms.layout.FRLayout2;
import edu.uci.ics.jung.algorithms.layout.KKLayout;
import edu.uci.ics.jung.algorithms.layout.Layout;
import edu.uci.ics.jung.algorithms.layout.SpringLayout;
import edu.uci.ics.jung.algorithms.layout.SpringLayout2;
import edu.uci.ics.jung.graph.DirectedSparseMultigraph;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.graph.util.EdgeType;
import edu.uci.ics.jung.visualization.DefaultVisualizationModel;
import edu.uci.ics.jung.visualization.GraphZoomScrollPane;
import edu.uci.ics.jung.visualization.VisualizationModel;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
import edu.uci.ics.jung.visualization.decorators.ToStringLabeller;

public class GraphPanel{
	
	private Graph<VfactVertex, VfactEdge> graph;
	private VisualizationViewer<VfactVertex, VfactEdge> viewer;
	private AggregateLayout<VfactVertex, VfactEdge> layout;
	
	@SuppressWarnings("rawtypes")
	private Class[] layoutClasses = new Class[]{CircleLayout.class, FRLayout.class, FRLayout2.class, 
			KKLayout.class, SpringLayout.class, SpringLayout2.class};
	
	//private Class<?> clusteringLayoutClass = CircleLayout.class;
	private String clusteringLayout = GraphFrame.FR;
	private Dimension clusteringLayoutSize = new Dimension(150, 150);
	private GraphZoomScrollPane gzsp;
	private List<MasterTermset> list;
	//private Timer timer;
	//private boolean showGraphDone, clusterDone, animation;
	int totalVertices = 0;
	int vertexIdx;
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
	Map<VfactVertex,Paint> vertexPaints = LazyMap.<VfactVertex,Paint>decorate(new HashMap<VfactVertex,Paint>(),
			new ConstantTransformer(Color.WHITE));
	
	public final Color[] colors = {new Color(216, 134, 134), new Color(135, 137, 211), new Color(134, 206, 189),
				new Color(206, 176, 134), new Color(194, 204, 134), new Color(145, 214, 134),
				new Color(133, 178, 209), new Color(103, 148, 255), new Color(60, 220, 220),
				new Color(30, 250, 100)
	};
	
	public void setupView(List<MasterTermset> list){

		this.list = list;
		graph = new DirectedSparseMultigraph<VfactVertex, VfactEdge>();
		
		layout = new AggregateLayout<VfactVertex, VfactEdge>(new FRLayout<VfactVertex, VfactEdge>(graph));
		Dimension preferredSize = new Dimension(600, 600);
		final VisualizationModel<VfactVertex, VfactEdge> visModel = new DefaultVisualizationModel<VfactVertex, VfactEdge>(layout, preferredSize);
		viewer = new VisualizationViewer<VfactVertex, VfactEdge>(visModel, preferredSize);
		viewer.setBackground(Color.WHITE);
		viewer.getRenderContext().setVertexFillPaintTransformer(MapTransformer.<VfactVertex,Paint>getInstance(vertexPaints));
		viewer.setVertexToolTipTransformer(new ToStringLabeller<VfactVertex>());
		final DefaultModalGraphMouse<VfactVertex, String> graphMouse = new DefaultModalGraphMouse<VfactVertex, String>();
		viewer.setGraphMouse(graphMouse);
		
		process(false);
		
		viewer.repaint();
		
		gzsp = new GraphZoomScrollPane(viewer);
		
	}
	
	public void process(boolean animation){
		if(animation == false){
			for(MasterTermset mt : list){
				graph.addVertex(mt.getTermset());
			}
			
			colorVertices(graph.getVertices(), Color.RED);
		}
	}
	
	public void setupSSAView(Map<TermSet, Set<Document>> ssaDict){
		graph = new DirectedSparseMultigraph<VfactVertex, VfactEdge>();
		
		int edgeId = 1;
		for(Map.Entry<TermSet, Set<Document>> entry : ssaDict.entrySet()){
			TermSet key = entry.getKey();
			Set<Document> value = entry.getValue();
			graph.addVertex(key);
			colorVertex(key, colors[1]);
			for(Document doc : value){
				graph.addVertex(doc);
				colorVertex(doc, colors[2]);
				graph.addEdge(new VfactEdge(edgeId, "Edge-" + edgeId), doc, key, EdgeType.DIRECTED);
			}
		}
		
		layout = new AggregateLayout<VfactVertex, VfactEdge>(new CircleLayout<VfactVertex, VfactEdge>(graph));
		
		viewer = new VisualizationViewer<VfactVertex, VfactEdge>(layout);
		viewer.setBackground(Color.WHITE);
		viewer.getRenderContext().setVertexFillPaintTransformer(MapTransformer.<VfactVertex,Paint>getInstance(vertexPaints));
		
		viewer.setVertexToolTipTransformer(new ToStringLabeller<VfactVertex>());
		
		final DefaultModalGraphMouse<VfactVertex, String> graphMouse = new DefaultModalGraphMouse<VfactVertex, String>();
		viewer.setGraphMouse(graphMouse);
		
		gzsp = new GraphZoomScrollPane(viewer);
	}
	
	public GraphZoomScrollPane getGZSP(){
		return this.gzsp;
	}
	
//	public void setClusteringLayoutClass(Class<?> layout){
//		clusteringLayoutClass = layout;
//	}
	
	public void setClusteringLayout(String layout){
		clusteringLayout = layout;
	}
	
	public Graph<VfactVertex, VfactEdge> getGraph(){
		return graph;
	}
	
	public VisualizationViewer<VfactVertex, VfactEdge> getViewer(){
		return viewer;
	}
	
	public AggregateLayout<VfactVertex, VfactEdge> getLayout(){
		return layout;
	}
	
	public void unclusterGraph(){
		
		layout.removeAll();
		viewer.setGraphLayout(layout);
		for(VfactVertex vertex : graph.getVertices()){
			Collection<VfactEdge> edges = graph.getIncidentEdges(vertex);
			if(edges != null){
				for(VfactEdge edge : edges){
					graph.removeEdge(edge);
				}
			}
		}
			
		colorVertices(graph.getVertices(), Color.RED);
	}
	
	public void clusterGraph(List<Cluster<VfactVertex>> clusters){
		
		int clusterIdx = 0;
		for(Cluster<VfactVertex> c : clusters){
			
			paintCluster(c, clusterIdx);
			clusterIdx++;
			
		}
	}
	
	private void paintCluster(Cluster<VfactVertex> cluster, int clusterIdx){
		Collection<VfactVertex> vertices = cluster.getAllVertices();
		VfactVertex prototype = cluster.getPrototype();
		Point2D center = new Point2D.Double();
		double x = 0.0;
		double y = 0.0;
		for(VfactVertex vertex : vertices){
			Point2D p = layout.transform(vertex);
			x += p.getX();
			y += p.getY();
		}
		
		x /= cluster.getSize();
		y /= cluster.getSize();
		center.setLocation(x, y);
		
		Graph<VfactVertex, VfactEdge> subgraph;
		try {
			int edgeId = 0;
			subgraph = new DirectedSparseMultigraph<VfactVertex, VfactEdge>();
			for(VfactVertex vertex : vertices){
				subgraph.addVertex(vertex);
				if(!(vertex.equals(prototype))){
					Color color = colors[clusterIdx % colors.length];
					colorVertex(vertex, color);
					graph.addEdge(new VfactEdge(++edgeId, "E-" + edgeId), vertex, prototype);
				}else{
					colorVertex(vertex, Color.YELLOW);
				}

			}
			
			//Layout<VfactVertex, VfactEdge> subLayout = getLayoutFor(clusteringLayoutClass, subgraph);
			Layout<VfactVertex, VfactEdge> subLayout = VizUtil.createLayoutByName(clusteringLayout, subgraph);
			subLayout.setInitializer(viewer.getGraphLayout());
			subLayout.setSize(clusteringLayoutSize);
			layout.put(subLayout, center);
			viewer.setGraphLayout(layout);
			
		} catch (Exception e) {
			e.printStackTrace();
		} 
	}
	
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public Layout<VfactVertex, VfactEdge> getLayoutFor(Class layoutClass, Graph<VfactVertex, VfactEdge> graph) throws Exception {
    	Object[] args = new Object[]{graph};
    	Constructor constructor = layoutClass.getConstructor(new Class[] {Graph.class});
    	return  (Layout)constructor.newInstance(args);
    }
	
	private void colorVertices(Collection<VfactVertex> vertices, Color color){
		for(VfactVertex vertex : vertices){
			vertexPaints.put(vertex, color);
		}
		
	}
	
	private void colorVertex(VfactVertex vertex, Color color){
		vertexPaints.put(vertex, color);
	}

}
